// This file was added for video encoding/decoding
#include "ImageSegment.h"
#include "Image.h"
#include <stdio.h>

ImageSegment::ImageSegment(): image(0), originx(0), originy(0), width(0), height(0)
{
}

ImageSegment::~ImageSegment()
{
}

void ImageSegment::SetImage(Image* parentimage)
{
	image = parentimage;
}

void ImageSegment::ClearImage()
{
	image = 0;
}

unsigned int ImageSegment::OriginX() const
{ 
	return originx; 
}

unsigned int ImageSegment::OriginY() const
{ 
	return originy; 
}

void ImageSegment::SetOriginX(unsigned int orgx) 
{ 
	originx = orgx;
}

void ImageSegment::SetOriginY(unsigned int orgy) 
{ 
	originy = orgy;
}

void ImageSegment::SetDimension(unsigned int newWidth, unsigned int newHeight)
{
	width = newWidth;
	height = newHeight;
}

void ImageSegment::ImportRaw(unsigned char *data, unsigned int size)
{
	if (!image)
		throw "ImageSegment: No image";

	if (size != Size())
		throw "ImageSegment: Cannot import raw data, size mismatch";

	for(unsigned int y=0; y<height; y++)
		for(unsigned int x=0; x<width; x++)
			SetComponent(x, y, data[y*width+x]);
}

void ImageSegment::ExportRaw(unsigned char *data, unsigned int size) const
{
	if (!image)
		throw "ImageSegment: No image";

	if (size != Size())
		throw "ImageSegment: Cannot export raw data, size mismatch";

	for(unsigned int y=0; y<height; y++)
		for(unsigned int x=0; x<width; x++)
			data[y*width+x] = GetComponent(x, y);
}

unsigned int ImageSegment::Width() const
{
	return width;
}

unsigned int ImageSegment::Height() const
{
	return height;
}

unsigned int ImageSegment::Size() const
{
	return width*height;
}

void ImageSegment::IncrementOriginX(signed int incx)
{
	originx += incx;
}

void ImageSegment::IncrementOriginY(signed int incy)
{
	originy += incy;
}

unsigned int ImageSegment::MAD(const ImageSegment& reference)
{
	if (reference.width != width || reference.height != height)
		throw "ImageSegment: Cannot calculate MAD of different segment dimensions";

	unsigned int mad = 0;

	for(unsigned int y=0; y<height; y++)
	{
		for(unsigned int x=0; x<width; x++)
		{
			unsigned int componentA = GetComponent(x, y);
			unsigned int componentB = reference.GetComponent(x, y);

			int diff = componentA - componentB;

			if (diff < 0)
				diff = -diff;

			mad += diff;
		}
	}

	mad /= width * height;

	return mad;
}

bool ImageSegment::PointInImage(signed int x, signed int y, const Image& rect)
{
	if (x >= 0 && x < rect.Width() && y >= 0 && y < rect.Height())
		return true;
	else
		return false;
}

bool ImageSegment::SegmentInImage(signed int x, signed int y, const Image& rect)
{
	if (PointInImage(x, y, rect) &&	PointInImage(x+width, y, rect) && PointInImage(x, y+height, rect) && PointInImage(x+width, y+height, rect))
		return true;
	else
		return false;
}

void ImageSegment::FitPointInImage(signed int *x, signed int *y, const Image& rect)
{
	if (*x < 0)
		*x = 0;
	if (*x > rect.Width())
		*x = rect.Width();

	if (*y < 0)
		*y = 0;
	if (*y > rect.Height())
		*y = rect.Height();
}


ImageSegment* ImageSegment::LocateSegment(Image& reference, unsigned int windowsize)
{
	ImageSegment *seg;
	unsigned int mad, minmad = ~0;

	int segcenterX = originx + width/2;
	int segcenterY = originy + height/2;

	int topleftX = segcenterX - (windowsize/2);
	int topleftY = segcenterY - (windowsize/2);
	int bottomrightX = segcenterX + (windowsize/2);
	int bottomrightY = segcenterY + (windowsize/2);
	int minX = -1, minY = -1;

	FitPointInImage(&topleftX, &topleftY, reference);
	FitPointInImage(&bottomrightX, &bottomrightY, reference);

	if (bottomrightX - topleftX < width || bottomrightY - topleftY < height)
		throw "ImageSegment: Windowsize too small";


	// Adjust bottomright point because the segment origin point is at the upper-left corner
	bottomrightX -= width;
	bottomrightY -= height;


	for(int y=topleftY; y<bottomrightY; y++)
	{
		for(int x=topleftX; x<bottomrightX; x++)
		{
			seg = reference.SegmentFromOffset(x, y, width, height);
			mad = MAD(*seg);


			if (mad < minmad)
			{
				minX = seg->OriginX();
				minY = seg->OriginY();
				minmad = mad;
			}
		}
	}



	return reference.SegmentFromOffset(minX, minY, width, height);
}


ImageSegment* ImageSegment::LocateSegmentLog(Image& reference, unsigned int windowsize)
{
	ImageSegment *seg, *minseg = 0;
	unsigned int mad, minmad = 0;

	throw "ImageSegment: LocateSegmentLog not implemented.";

	/*

	// NOTE: Needs to use minX and minY instead of minseg. Also, this algorithm is UNTESTED.



	static const signed int direction[8][2]=
	{
		{-1, -1}, { 0, -1}, { 1, -1},
		{-1,  0},           { 1,  0},
		{-1,  1}, { 0,  1}, { 1,  1},
	};

	// At first, the minimum mad is the same-origin block in the reference image
	if (SegmentInImage(originx, originy, reference))
	{
		minseg = reference.SegmentFromOffset(originx, originy, width, height);
		minmad = MAD(*seg);
	}


	while( (windowsize/2) > 1 )
	{
		for(unsigned int d=0; d<8; d++)
		{
			int offsetx = (signed int)originx + direction[d][0]*windowsize/2;
			int offsety = (signed int)originy + direction[d][1]*windowsize/2;

			if (SegmentInImage(offsetx, offsety, reference))
			{
				seg = reference.SegmentFromOffset(offsetx, offsety, width, height);
				mad = MAD(*seg);

				if (minseg == 0 || mad < minmad)
				{
					minseg = seg;
					minmad = mad;
				}
			}
		}

		windowsize /= 2;
	}
	*/

	return minseg;
}

void ImageSegment::ReadRawFromSegment(const ImageSegment& seg)
{
	if (seg.width != width || seg.height != height)
		throw "ImageSegment: Dimension mismatch";

	for(unsigned int y=0; y<height; y++)
		for(unsigned int x=0; x<width; x++)
			SetComponent(x, y, seg.GetComponent(x, y));
}

void ImageSegment::ReadRawFromImage(Image& im)
{
	if (im.Width() != width || im.Height() != height)
		throw "ImageSegment: Dimension mismatch";

	ImageSegment *seg = im.SegmentFromOffset(0, 0, Width(), Height());
	if (!seg)
		throw "ImageSegment: Invalid segment";
	ReadRawFromSegment(*seg);
}

void ImageSegment::Substract(const ImageSegment& subseg)
{
	if (subseg.width != width || subseg.height != height)
		throw "ImageSegment: Cannot substract different segment dimensions";


	for(unsigned int y=0; y<height; y++)
		for(unsigned int x=0; x<width; x++)
		{
			int componentA = (int)GetComponent(x, y);
			int componentB = (int)subseg.GetComponent(x, y);

			componentA = componentA - componentB;

			if (componentA < -128)
				componentA = -128;
			else if (componentA > 127)
				componentA = 127;

			SetComponent(x, y, 128 + componentA );
		}
}

void ImageSegment::Add(const ImageSegment& subseg)
{
	if (subseg.width != width || subseg.height != height)
		throw "ImageSegment: Cannot add different segment dimensions";


	for(unsigned int y=0; y<height; y++)
		for(unsigned int x=0; x<width; x++)
		{
			int componentA = (int)GetComponent(x, y);
			int componentB = (int)subseg.GetComponent(x, y);

			componentA = componentA-128 + componentB;

			if (componentA < 0)
				componentA = 0;
			else if (componentA > 255)
				componentA = 255;

			SetComponent(x, y, componentA);
		}
}


unsigned char ImageSegment::GetComponent(unsigned int x, unsigned int y) const
{
	if (!image)
		throw "ImageSegment: No image";

	return image->GetComponent(originx+x, originy+y);
}

void ImageSegment::SetComponent(unsigned int x, unsigned int y, unsigned char intensity)
{
	if (!image)
		throw "ImageSegment: No image";

	image->SetComponent(originx+x, originy+y, intensity);
}